#include "config/config_parser.h"

#include <iostream>
#include <stack>

#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/spirit.hpp>

#include "config/config.h"

using namespace boost::spirit;
using namespace phoenix;
using namespace pita;
using namespace std;

namespace
{

	struct ConfigGrammar : public grammar<ConfigGrammar>
	{
			template <typename ScannerT>
			struct definition
			{
					definition(ConfigGrammar const& self)
						{
							typedef typename ScannerT::iterator_t Itr;

							boost::function<void(Itr, Itr)> push_key =
								boost::bind(&ConfigGrammar::push_key<Itr>, &self, _1, _2);

							boost::function<void(Itr, Itr)> bind_value =
								boost::bind(&ConfigGrammar::bind_value<Itr>, &self, _1, _2);
							
							boost::function<void(const char&)> push_group =
								boost::bind(&ConfigGrammar::push_group, &self, _1);

							boost::function<void(const char&)> pop_group =
								boost::bind(&ConfigGrammar::pop_group, &self, _1);
							

							config 
								= *(binding);
								;
							
							key // alpha-numerics and underscore
								= +(alnum_p 
									| ch_p('_')
									)
								;
							
							value // anything except an end-of-line
								= lexeme_d[*(anychar_p - eol_p)] 
								;
							
							binding
								= key
								[
									push_key
								]
								>> ( 
									('=' >> value[bind_value] >> eol_p)
									| group
								   )
									 
								;

							group
								= ch_p('{')[push_group] >> eol_p
								>> config
								>> ch_p('}')[pop_group] >> eol_p
								;
								 
						}
					
					rule<ScannerT> config, key, value, binding, group;
					
					rule<ScannerT> const&
					start() const { return config; }

					
			};
			
			ConfigGrammar(Config& cfg)
				{
					configs.push(&cfg);
				}

			mutable stack<Config*> configs;
			mutable stack<string> keys;

			template <typename Itr>
			void push_key(Itr first, Itr last) const
				{
					keys.push(std::string(first, last));
				}

			template <typename Itr>
			void bind_value(Itr first, Itr last) const 
				{
					assert(!configs.empty());
					assert(!keys.empty());

					configs.top()->insert(keys.top(), string(first, last));
					keys.pop();
				}

			void push_group(const char&) const
				{
					assert(!configs.empty());
					assert(!keys.empty());
					
					Config::group_iterator gitr =
						configs.top()->insertGroup(keys.top());
					
					configs.push(&*gitr);

					keys.pop();
				}

			void pop_group(const char&) const
				{
					assert(!configs.empty());

					configs.pop();
				}
	};

}

bool pita::parseConfig(const std::string& fname, 
					   pita::Config& cfg)
{
	ConfigGrammar g(cfg);

	file_iterator<> first(fname);
	
    if (!first)
    {
		// TODO: Throw or something better...log something...
		return false;
	}
    file_iterator<> last = first.make_end();

	return parse(first, last, g, blank_p).full;
}
